package exercises

import scala.collection.mutable.ListBuffer

object Lists extends Application {

  def flatten(list: List[Any]): List[Any] = list.flatMap {
    case innerList: List[_] => flatten(innerList)
    case e => List(e)
  }

  // TODO: ¿Cuál es la diferencia entre hacer List[_] y List[Any]?
  def isPalindrome(list: List[_]): Boolean = list == list.reverse

  def compress(list: List[Symbol]): List[Symbol] = list match {
    case Nil => Nil
    case h :: tail => h :: compress(tail.dropWhile(_ == h))
  }

  def pack[A](list: List[A]): List[List[A]] = {
    if (list.isEmpty) Nil else {
      val (packed, tail) = list span (_ == list.head)
      packed :: pack(tail)
    }
  }

  def encode[A](list: List[A]): List[(Int, A)] = pack(list).map { l => (l.size, l.head) }

  def encodeModified[A](list: List[A]): List[Either[A, (Int, A)]] = encode(list).map { case (n, e) => if (n == 1) Left(e) else Right((n, e)) }

  def decode[A](list: List[(Int, A)]): List[A] = list flatMap { case (n, e) => List.make(n, e) }

  def encodeDirect[A](list: List[A]): List[(Int, A)] = {
    if (list.isEmpty) Nil
    else {
      val (packed, tail) = list.span { _ == list.head }
      (packed.length, packed.head) :: encodeDirect(tail)
    }
  }

  def duplicate[A](list: List[A]): List[A] = list.flatMap { a => List(a, a) }

  def duplicateN[A](n: Int, list: List[A]): List[A] = list.flatMap { List.make(n, _) }

  def drop[A](n: Int, list: List[A]): List[A] = list.zipWithIndex filter { case (a, i) => (i + 1) % n != 0 } map { _._1 }

  def split[A](n: Int, list: List[A]): (List[A], List[A]) = (list take n, list drop n)
  def slice[A](i: Int, k: Int, list: List[A]): List[A] = split(k - i, split(i, list)._2)._1

  //TODO: def rotate[A](n: Int, list: List[A]): List[A] = if n <= 0 list else rotate(n-1, list.tail ++ List(list.head))

  def removeAt[A](i: Int, list: List[A]): (List[A], A) = (list.take(i) ++ list.drop(i + 1), list(i))

  override def main(args: Array[String]) {
    println(flatten(List(List(1, 1), 2, List(3, List(5, 8))))) // List(1, 1, 2, 3, 5, 8)
    println(isPalindrome(List(1, 2, 1)) + " " + isPalindrome(List(1, 2, 2, 1)) + " " + isPalindrome(List(1, 2, 2)))
    println(compress(List('a, 'a, 'a, 'a, 'b, 'c, 'c, 'a, 'a, 'd, 'e, 'e, 'e, 'e)))
    println(pack(List('a, 'a, 'a, 'a, 'b, 'c, 'c, 'a, 'a, 'd, 'e, 'e, 'e, 'e)))
    println(encode(List('a, 'a, 'a, 'a, 'b, 'c, 'c, 'a, 'a, 'd, 'e, 'e, 'e, 'e)))
    println(encodeModified(List('a, 'a, 'a, 'a, 'b, 'c, 'c, 'a, 'a, 'd, 'e, 'e, 'e, 'e)))
    println(decode(List((4, 'a), (1, 'b), (2, 'c), (2, 'a), (1, 'd), (4, 'e))))
    println(encodeDirect(List('a, 'a, 'a, 'a, 'b, 'c, 'c, 'a, 'a, 'd, 'e, 'e, 'e, 'e)))
    println(duplicate(List('a, 'b, 'c, 'c, 'd)))
    println(duplicateN(3, List('a, 'b, 'c, 'c, 'd)))
    println(drop(3, List('a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k)))
    println(split(3, List('a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k)))
    println(split(3, List('a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k)))
    println(slice(3, 7, List('a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k)))
    //println(rotate(3, List('a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k)))
    println(removeAt(1, List('a, 'b, 'c, 'd)))
  }
}